package maple.thelittleredbook.recommendation.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import maple.thelittleredbook.content.entity.Content;
import maple.thelittleredbook.content.mapper.ContentMapper;
import maple.thelittleredbook.recommendation.entity.ContentFeature;
import maple.thelittleredbook.recommendation.mapper.ContentFeatureMapper;
import maple.thelittleredbook.recommendation.utils.LLMServiceUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;

/**
 * 异步内容特征生成服务
 * 单独的Service文件确保@Async注解生效
 */
@Slf4j
@Service
public class AsyncContentFeatureService {

    private final ContentFeatureMapper contentFeatureMapper;
    private final ContentMapper contentMapper;
    private final LLMServiceUtil llmServiceUtil;
    private final ObjectMapper objectMapper;

    // 系统提示词 - 您可以根据需要调整
    private static final String SYSTEM_PROMPT = """
        你是一个专业的内容分析专家。请分析用户提供的内容，按照8个维度进行评分。
        每个维度的分值范围为-1.00到1.00（保留两位小数）。
        
        详细维度定义：
        
        1. dim1 - 具象vs抽象（-1.00=完全具象，1.00=完全抽象）
           - 具象(-1.00到-0.40): 具体的事物、明确的数据、实际案例、可感知的现象
           - 中性(-0.39到0.39): 既有具体描述又有概念阐述
           - 抽象(0.40到1.00): 理论概念、哲学思辨、原理法则、思想观念
        
        2. dim2 - 理性vs情感（-1.00=完全理性，1.00=完全情感）
           - 理性(-1.00到-0.40): 客观分析、逻辑推理、数据支撑、事实陈述
           - 中性(-0.39到0.39): 理性分析中带有情感表达
           - 情感(0.40到1.00): 个人感受、情绪表达、主观判断、感性描述
        
        3. dim3 - 系统vs探索（-1.00=完全系统，1.00=完全探索）
           - 系统(-1.00到-0.40): 结构化知识、成熟理论、标准流程、既定框架
           - 中性(-0.39到0.39): 在现有基础上的延伸思考
           - 探索(0.40到1.00): 创新想法、未来展望、实验性思考、开放性讨论
        
        4. dim4 - 个人vs社群（-1.00=完全个人，1.00=完全社群）
           - 个人(-1.00到-0.40): 个体经历、私人感受、个人技能、自我反思
           - 中性(-0.39到0.39): 个人与社会的结合
           - 社群(0.40到1.00): 集体利益、社会现象、群体行为、公共议题
        
        5. dim5 - 专业vs通俗（-1.00=完全专业，1.00=完全通俗）
           - 专业(-1.00到-0.40): 专业术语多、技术细节、学术语言、行业知识
           - 中性(-0.39到0.39): 专业内容但表达相对易懂
           - 通俗(0.40到1.00): 日常用语、通俗比喻、大众化表达、易于理解
        
        6. dim6 - 经典vs新潮（-1.00=完全经典，1.00=完全新潮）
           - 经典(-1.00到-0.40): 传统观念、历史内容、成熟技术、经典理论
           - 中性(-0.39到0.39): 传统与现代的结合
           - 新潮(0.40到1.00): 最新技术、前沿概念、时尚趋势、创新理念
        
        7. dim7 - 实用vs娱乐（-1.00=完全实用，1.00=完全娱乐）
           - 实用(-1.00到-0.40): 工具性知识、操作指南、问题解决、技能学习
           - 中性(-0.39到0.39): 寓教于乐的内容
           - 娱乐(0.40到1.00): 消遣性内容、趣味性描述、娱乐性元素、轻松阅读
        
        8. dim8 - 极简vs丰富（-1.00=完全极简，1.00=完全丰富）
           - 极简(-1.00到-0.40): 简洁明了、核心要点、精炼表达、信息量少
           - 中性(-0.39到0.39): 适中的信息量和细节
           - 丰富(0.40到1.00): 内容详实、多层次、多角度、信息量大
        
        评分要求：
        1. 仔细阅读内容，理解其主要特征
        2. 根据上述定义对每个维度进行-1.00到1.00评分
        3. 0.00表示该维度上完全中性，没有明显倾向
        4. 负值表示倾向于维度的左端，正值表示倾向于维度的右端
        5. 评分要体现内容的主要特征，如果特征明显应给出相应的强度分值
        
        请严格按照以下JSON格式返回结果，不要包含任何其他文字解释：
        {
            "dim1": 0.30,
            "dim2": -0.60,
            "dim3": 0.80,
            "dim4": -0.20,
            "dim5": 0.45,
            "dim6": 0.70,
            "dim7": -0.15,
            "dim8": 0.55
        }
        """;

    @Autowired
    public AsyncContentFeatureService(ContentFeatureMapper contentFeatureMapper,
                                      ContentMapper contentMapper,
                                      LLMServiceUtil llmServiceUtil,
                                      ObjectMapper objectMapper) {
        this.contentFeatureMapper = contentFeatureMapper;
        this.contentMapper = contentMapper;
        this.llmServiceUtil = llmServiceUtil;
        this.objectMapper = objectMapper;
    }

    /**
     * 异步处理单个内容特征生成任务
     *
     * @param contentFeatureId 内容特征ID
     * @param semaphore 信号量（用于控制并发数量）
     */
    @Async
    public void processSingleTaskAsync(Long contentFeatureId, Semaphore semaphore) {
        try {
            log.info("开始异步处理内容特征任务，ID: {}", contentFeatureId);

            // 1. 获取内容特征记录
            ContentFeature contentFeature = contentFeatureMapper.selectById(contentFeatureId);
            if (contentFeature == null) {
                log.error("内容特征记录不存在，ID: {}", contentFeatureId);
                return;
            }

            // 2. 获取对应的内容信息
            Content content = contentMapper.selectById(contentFeature.getContentId());
            if (content == null) {
                updateTaskFailed(contentFeatureId, "对应的内容记录不存在，contentId: " + contentFeature.getContentId());
                return;
            }

            // 3. 构建用户消息内容
            String userMessage = buildUserMessage(content);
            log.debug("构建用户消息完成，内容长度: {}", userMessage.length());

            // 4. 调用LLM分析
            String llmResponse = callLLMAnalysis(userMessage);
            if (llmResponse == null) {
                updateTaskFailed(contentFeatureId, "LLM调用返回空结果");
                return;
            }

            // 5. 解析JSON结果
            Map<String, BigDecimal> features = parseFeatureResponse(llmResponse);
            if (features == null) {
                updateTaskFailed(contentFeatureId, "LLM返回结果解析失败，原始内容: " + llmResponse);
                return;
            }

            // 6. 更新数据库
            updateTaskCompleted(contentFeatureId, features);
            log.info("内容特征任务处理完成，ID: {}", contentFeatureId);

        } catch (Exception e) {
            log.error("处理内容特征任务时发生异常，ID: {}", contentFeatureId, e);
            updateTaskFailed(contentFeatureId, "处理异常: " + e.getMessage());
        } finally {
            // 7. 释放信号量
            if (semaphore != null) {
                semaphore.release();
                log.debug("释放信号量，当前可用数量: {}", semaphore.availablePermits());
            }
        }
    }

    /**
     * 构建用户消息内容
     */
    private String buildUserMessage(Content content) {
        StringBuilder sb = new StringBuilder();

        if (content.getTitle() != null && !content.getTitle().trim().isEmpty()) {
            sb.append("标题：").append(content.getTitle().trim()).append("\n");
        }

        if (content.getTextContent() != null && !content.getTextContent().trim().isEmpty()) {
            sb.append("内容：").append(content.getTextContent().trim());
        }

        return sb.toString();
    }

    /**
     * 调用LLM分析
     */
    private String callLLMAnalysis(String userMessage) {
        try {
            return llmServiceUtil.callChatCompletion(Arrays.asList(
                    new LLMServiceUtil.ChatMessage("system", SYSTEM_PROMPT),
                    new LLMServiceUtil.ChatMessage("user", userMessage)
            ));
        } catch (Exception e) {
            log.error("LLM调用失败", e);
            return null;
        }
    }

    /**
     * 解析LLM返回的特征结果
     */
    private Map<String, BigDecimal> parseFeatureResponse(String response) {
        try {
            // 提取JSON部分
            String jsonStr = extractJsonFromResponse(response);

            // 解析JSON
            @SuppressWarnings("unchecked")
            Map<String, Object> jsonMap = objectMapper.readValue(jsonStr, Map.class);

            // 转换为BigDecimal并验证
            Map<String, BigDecimal> features = new HashMap<>();
            for (int i = 1; i <= 8; i++) {
                String key = "dim" + i;
                Object value = jsonMap.get(key);

                if (value == null) {
                    log.error("缺少维度值: {}", key);
                    return null;
                }

                BigDecimal dimValue;
                if (value instanceof Number) {
                    dimValue = BigDecimal.valueOf(((Number) value).doubleValue());
                } else {
                    dimValue = new BigDecimal(value.toString());
                }

                // 验证范围 [-1.00, 1.00]
                if (dimValue.compareTo(new BigDecimal("-1.00")) < 0 || dimValue.compareTo(BigDecimal.ONE) > 0) {
                    log.error("维度值超出范围[-1.00-1.00]: {}={}", key, dimValue);
                    return null;
                }

                features.put(key, dimValue);
            }

            return features;

        } catch (Exception e) {
            log.error("解析特征结果失败", e);
            return null;
        }
    }

    /**
     * 从响应中提取JSON字符串
     */
    private String extractJsonFromResponse(String response) {
        int startIndex = response.indexOf("{");
        int endIndex = response.lastIndexOf("}");

        if (startIndex == -1 || endIndex == -1 || startIndex >= endIndex) {
            throw new RuntimeException("响应中未找到有效的JSON格式");
        }

        return response.substring(startIndex, endIndex + 1);
    }

    /**
     * 更新任务为完成状态
     */
    private void updateTaskCompleted(Long contentFeatureId, Map<String, BigDecimal> features) {
        try {
            int updateCount = contentFeatureMapper.updateTaskCompleted(
                    contentFeatureId,
                    features.get("dim1"),
                    features.get("dim2"),
                    features.get("dim3"),
                    features.get("dim4"),
                    features.get("dim5"),
                    features.get("dim6"),
                    features.get("dim7"),
                    features.get("dim8")
            );

            if (updateCount != 1) {
                log.error("更新任务完成状态失败，影响行数: {}, ID: {}", updateCount, contentFeatureId);
            }
        } catch (Exception e) {
            log.error("更新任务完成状态时发生异常，ID: {}", contentFeatureId, e);
        }
    }

    /**
     * 更新任务为失败状态
     */
    private void updateTaskFailed(Long contentFeatureId, String errorMessage) {
        try {
            // 限制错误信息长度
            String trimmedError = errorMessage.length() > 250 ? errorMessage.substring(0, 250) + "..." : errorMessage;

            int updateCount = contentFeatureMapper.updateTaskFailed(contentFeatureId, trimmedError);

            if (updateCount != 1) {
                log.error("更新任务失败状态失败，影响行数: {}, ID: {}", updateCount, contentFeatureId);
            }
        } catch (Exception e) {
            log.error("更新任务失败状态时发生异常，ID: {}", contentFeatureId, e);
        }
    }
}